package com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.MessageSenderContext;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessInstanceLinkStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessInstanceStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.TaskLinkStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.CallbackThirdPlatformAfterTaskCreatedService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.MessageSenderManager;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.MyApplyOperateService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.RemindCheckService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.MessageFillUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.MsgTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowProcessInstanceTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ParticipantVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.User;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.monitor.FlowTaskTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowNoticeTemplateVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowNoticeTemplateRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowProcessInstanceTraceRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowTaskTraceRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.UserRepository;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cmd.AddCommentCmd;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author lilh
 * @date 2020/12/1 下午1:46
 */
@Service
public class MyApplyOperateServiceImpl implements MyApplyOperateService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private FlowProcessInstanceTraceRepository processInstanceTraceRepository;

    @Autowired
    private TaskService taskService;

    @Autowired
    private CallbackThirdPlatformAfterTaskCreatedService participantService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private FlowNoticeTemplateRepository noticeTemplateRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private MessageSenderManager senderManager;

    @Autowired
    private RemindCheckService remindCheckService;

    @Autowired
    private FlowTaskTraceRepository taskTraceRepository;


    @Override
    public boolean remind(String processInstanceId) {
        if (checkAndSendMsg(processInstanceId, MsgTypeEnum.TASK_REMIND)) {
            return false;
        }
        //将操作标识写入redis
        //remindCheckService.setRemindTag(processInstanceId, resolveRunningTask(processInstanceId));
        remindCheckService.setRemindTag(processInstanceId);
        return true;
    }

    private String resolveRunningTask(String processInstanceId) {
        List<FlowTaskTraceVo> runningTasks = taskTraceRepository.listRunningTaskTraces(processInstanceId);
        if (CollectionUtils.isNotEmpty(runningTasks)) {
            //获取第一个任务，可能某些场景会不太对
            return runningTasks.get(0).getTaskId();
        }
        return null;
    }

    @Override
    public boolean recall(String processInstanceId, String comment) {
        if (checkAndSendMsg(processInstanceId, MsgTypeEnum.PROCESS_INSTANCE_RECALL)) {
            return false;
        }

        //6.终止流程
        saveComment(processInstanceId, comment);

        //7.更新为实例状态为撤回
        updateProcessInstanceStatus(processInstanceId);
        return true;

    }

    private void doSendMessage(Map<String, List<ParticipantVo>> taskToParticipantMap, Map<String, MessageSenderContext> taskIdToContextMap) {
        for (String taskId : taskToParticipantMap.keySet()) {
            MessageSenderContext context = taskIdToContextMap.get(taskId);
            if (Objects.isNull(context)) {
                continue;
            }
            List<ParticipantVo> participants = taskToParticipantMap.get(taskId);
            for (ParticipantVo participant : participants) {
                MessageSenderContext newContext = MessageSenderContext.builder().build();
                BeanUtils.copyProperties(context, newContext);
                newContext.setReceiverCode(participant.getParticipantId());
                newContext.setReceiverName(participant.getParticipantName());
                senderManager.send(newContext);
            }
        }
    }

    private Map<String, MessageSenderContext> resolveMessageContext(String processInstanceId, String type, List<TaskEntity> tasks) {
        FlowNoticeTemplateVo template = resolveNoticeTemplate(type);
        if (Objects.isNull(template)) {
            return null;
        }

        User current = userRepository.getCurrentUser();
        Map<String, MessageSenderContext> result = new HashMap<>(tasks.size());
        FlowProcessInstanceTraceVo pt = processInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstanceId);
        for (TaskEntity task : tasks) {
            //解析模板信息
            Map<String, Object> params = resolveTemplateParams(pt, task);
            MessageSenderContext context = MessageSenderContext.builder().senderCode(current.getUserCode()).senderName(current.getUserName())
                    .title(MessageFillUtils.replaceWithMap(template.getTitle(), params))
                    .body(MessageFillUtils.replaceWithMap(template.getContent(), params))
                    .type(type)
                    .processName(pt.getModelName())
                    .processInstanceId(pt.getProcessInstanceId())
                    .taskId(task.getId())
                    .build();
            result.put(task.getId(), context);
        }

        return result;
    }

    private Map<String, Object> resolveTemplateParams(FlowProcessInstanceTraceVo pt, TaskEntity task) {

        Map<String, Object> result = new HashMap<>(8);
        result.put("taskName", task.getName());
        result.put("processName", pt.getModelName());
        result.put("originator", pt.getStarterName());
        return result;
    }

    private FlowNoticeTemplateVo resolveNoticeTemplate(String type) {
        List<FlowNoticeTemplateVo> templateList = noticeTemplateRepository.getTemplateListByType(type);
        if (CollectionUtils.isNotEmpty(templateList)) {
            return templateList.get(0);
        }
        return null;
    }

    private Boolean checkAndSendMsg(String processInstanceId, MsgTypeEnum type) {
        List<TaskEntity> tasks = new ArrayList<>();
        if (checkAndResolveCurrentTasks(processInstanceId, tasks)) {
            return true;
        }

        //3.查询任务参与人
        Map<String, List<ParticipantVo>> taskToParticipantMap = resolveTaskParticipants(tasks);


        //4.获取消息模板和填充消息
        Map<String, MessageSenderContext> taskIdToContextMap = resolveMessageContext(processInstanceId, type.getCode(), tasks);
        if (Objects.isNull(taskIdToContextMap)) {
            logger.info("未配置{}模板", type.getDescription());
            return false;
        }

        //5.发送消息
        doSendMessage(taskToParticipantMap, taskIdToContextMap);
        return false;
    }

    private void updateProcessInstanceStatus(String processInstanceId) {
        processInstanceTraceRepository.updateStatus(processInstanceId, ProcessInstanceStatusEnum.RECALL.getCode());
        processInstanceTraceRepository.updateLinkStatusToEnd(processInstanceId);
        updateTaskStatus(processInstanceId);
    }

    private void updateTaskStatus(String processInstanceId) {
        List<FlowTaskTraceVo> taskTraces = taskTraceRepository.listByProcessInstanceId(processInstanceId);
        List<FlowTaskTraceVo> runningTasks = taskTraces.stream().filter(item -> Objects.isNull(item.getCompleteTime())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(runningTasks)) {
            for (FlowTaskTraceVo task : runningTasks) {
                task.setStatus(ProcessInstanceStatusEnum.RECALL.getCode());
                task.setLinkStatus(TaskLinkStatusEnum.DONE.getCode());
                task.setCompleteTime(new Date());
                task.setUpdateTime(new Date());
                taskTraceRepository.updateTaskTraceById(task);
            }
        }
    }

    private void saveComment(String processInstanceId, String comment) {
        managementService.executeCommand(new AddCommentCmd(null, processInstanceId, "deleteComment", comment));
        runtimeService.deleteProcessInstance(processInstanceId, comment);
    }

    private boolean checkAndResolveCurrentTasks(String processInstanceId, List<TaskEntity> tasks) {
        //1.获取流程实例，判断是否已完成，若已完成，则不进行任何操作
        FlowProcessInstanceTraceVo processInstanceTrace = processInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstanceId);
        if (Objects.isNull(processInstanceTrace) || isDone(processInstanceTrace)) {
            logger.info("流程{}不存在或已结束", processInstanceId);
            return true;
        }
        //2.获取当前运行的任务，遍历发送消息
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        tasks.addAll(taskList.stream().map(item -> (TaskEntity) item).collect(Collectors.toList()));
        if (CollectionUtils.isEmpty(tasks)) {
            logger.info("当前没有运行中的任务");
            return true;
        }
        return false;
    }

    private Map<String, List<ParticipantVo>> resolveTaskParticipants(List<TaskEntity> tasks) {
        Map<String, List<ParticipantVo>> result = new HashMap<>(tasks.size());
        for (TaskEntity task : tasks) {
            List<ParticipantVo> participants = participantService.resolveParticipants(task);
            if (CollectionUtils.isNotEmpty(participants)) {
                //先只向人员发送消息
                result.put(task.getId(), participants.stream().filter(item -> Objects.equals(item.getParticipantType(), "USR")).collect(Collectors.toList()));
            }
        }
        return result;
    }

    private boolean isDone(FlowProcessInstanceTraceVo processInstanceTrace) {
        return Objects.equals(ProcessInstanceLinkStatusEnum.END.getCode(), processInstanceTrace.getLinkStatus());
    }
}
